package com.dj.mingkong.common.util;

import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT工具类
 */
@Slf4j
@ConfigurationProperties("jwt.config")
public class JwtUtil {

    private String key; //秘钥

    private long ttl;// 过期的时长

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public long getTtl() {
        return ttl;
    }

    public void setTtl(long ttl) {
        this.ttl = ttl;
    }

    //存入token内的用户名的kye
    private static final String CLAIM_KEY_USERINFO = "sub";
    //存入token的创建时间
    private static final String CLAIM_KEY_CREATED = "created";
    private static final String ROLE_KEY = "roles";

    /**
     * 生成JWT 签发JWT
     *
     * @param id      JWT_id
     * @param subject 主题
     * @param roles   角色
     * @return
     */
    public String createJwt(String id, String subject, String roles) {
        //这里其实就是new一个JwtBuilder，设置jwt的body
        JwtBuilder builder = Jwts.builder()
                //设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
                .setId(id)
                //sub(Subject)：代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志。
                .setSubject(subject)
                //iat: jwt的签发时间
                .setIssuedAt(generateNowDate())
                //设置签名使用的签名算法和签名使用的秘钥
                .signWith(SignatureAlgorithm.HS512, key)
                //自定义的属性(一般定义角色)
                .claim(ROLE_KEY, roles);

        if (ttl > 0) {
            //设置过期时间
            builder.setExpiration(generateExpirationDate(ttl));
        }
        //返回Jwts
        return builder.compact();
    }

    /**
     * 生成token的签发时间
     */
    private Date generateNowDate() {
        return new Date(System.currentTimeMillis());
    }

    /**
     * 根据用户信息生成token
     */
    public String generateToken(String info, long expiratTime) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_USERINFO, info);
        claims.put(CLAIM_KEY_CREATED, DateUtils.getNowDate());
        return generateToken(claims, expiratTime);
    }

    /**
     * 判断token是否过期  true:过期  false:未过期
     */
    public boolean isTokenExpired(String token) {
        Date expiredDate = getExpiredDateFromToken(token);
        return expiredDate.before(DateUtils.getNowDate());
    }

    /**
     * 判断token是否过期  true:过期  false:未过期
     */
    public boolean isAuthTokenExpired(String token) {
        Date expiredDate = getExpiredDateFromAuthToken(token);
        return expiredDate.before(DateUtils.getNowDate());
    }


    /**
     * 从token中获取用户信息
     */
    public <T> T getUserInfoFromToken(String token, Class<T> clazz) {
        T userInfo = null;
        try {
            Claims claims = getClaimsFromToken(token);
            String userInfoStr = claims.getSubject();
            log.info("userInfoStr: {}", userInfoStr);
            if (StringUtils.isNotBlank(userInfoStr)) {
                userInfo = JSON.parseObject(userInfoStr, clazz);
            }

        } catch (Exception e) {
            log.warn("解析toke信息失败! token:{}", token);
        }
        return userInfo;
    }

    /**
     * 刷新token
     */
    public String refreshToken(String token, long expiratTime) {
        Claims claims = getClaimsFromToken(token);
        claims.put(CLAIM_KEY_CREATED, DateUtils.getNowDate());
        return generateToken(claims, expiratTime);
    }

    /**
     * 根据负责生成JWT的token
     */
    private String generateToken(Map<String, Object> claims, long expiratTime) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate(expiratTime))
                .signWith(SignatureAlgorithm.HS512, key)
                .compact();
    }

    /**
     * 从token中获取JWT中的负载
     */
    private Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser()
                    .setSigningKey(key)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (ExpiredJwtException e) {
            log.warn("解析toke信息失败! token:{}", token);
            claims = e.getClaims();
        }
        return claims;
    }

    /**
     * 从Auth token中获取JWT中的负载
     */
    private Claims getClaimsFromAuthToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser()
                    .setSigningKey(key)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (ExpiredJwtException e) {
            log.warn("[AuthToken]解析toke信息失败!");
            claims = e.getClaims();
        }
        return claims;
    }

    /**
     * 生成token的过期时间
     */
    private static Date generateExpirationDate(long expiratTime) {
        return new Date(System.currentTimeMillis() + expiratTime);
    }

    /**
     * 从token中获取过期时间
     */
    private Date getExpiredDateFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.getExpiration();
    }

    /**
     * 从Auth token中获取过期时间
     */
    private Date getExpiredDateFromAuthToken(String token) {
        Claims claims = getClaimsFromAuthToken(token);
        return claims.getExpiration();
    }

    /**
     * 不要用于业务，仅用于测试用例
     **/
    public Date getCreateDate(String token) {
        Claims claims = getClaimsFromToken(token);
        return new Date((Long) claims.get(CLAIM_KEY_CREATED));
    }

}
